package com.qianniu.napi.common.util;

import com.google.common.base.Strings;
import com.qianniu.napi.admin.service.IRedisService;
import com.qianniu.napi.common.config.Constants;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

/**
 * Created by chenxuebin on 2018/6/6.
 * <p>
 * <p>
 * AES-128-CBC 加密方式
 * 注：
 * AES-128-CBC可以自己定义“密钥”和“偏移量“。
 * AES-128是jdk自动生成的“密钥”。
 */
public class AesUtil {

    private static final Logger logger = LoggerFactory.getLogger(AesUtil.class);
    private static final String KEY_ALGORITHM = "AES";
    private static final String CIPHER_ALGORITHM_CBC7 = "AES/CBC/PKCS7Padding";//默认的加密算法
    private static final String DEFAULT_CHARSET = "UTF-8";

    private static String key = "8wCN5ZMLiWklI1WnZ3/rYg==";
    private static String iv = "Nu2jEK/Ox9uLRxPvmgV5kg==";

    static {
        //BouncyCastle是一个开源的加解密解决方案，主页在http://www.bouncycastle.org/
        // 提供CIPHER_ALGORITHM_CBC加密解密的实现（秘钥+偏移量方式）
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * AES加密(秘钥+偏移量)
     *
     * @param data //密文，被加密的数据
     * @param key  //秘钥
     * @param iv   //偏移量
     * @return
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {

        //加密秘钥
        byte[] keyByte = Base64.decodeBase64(key);
        //偏移量
        byte[] ivByte = Base64.decodeBase64(iv);

        try {

            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }

            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC7, "BC");

            AlgorithmParameters parameters = AlgorithmParameters.getInstance(KEY_ALGORITHM);
            parameters.init(new IvParameterSpec(ivByte));

            SecretKeySpec spec = new SecretKeySpec(keyByte, KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, spec, parameters);// 初始化

            byte[] resultByte = cipher.doFinal(data.getBytes(DEFAULT_CHARSET));
            if (null != resultByte && resultByte.length > 0) {
                return Base64.encodeBase64String(resultByte);
            }

            return null;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * AES解密(秘钥+偏移量)
     *
     * @param data //密文，被加密的数据
     * @param key  //秘钥
     * @param iv   //偏移量
     * @return
     * @throws Exception
     */
    public static String decrypt(String data, String key, String iv, String rkey) throws Exception {
        byte[] dataByte = Base64.decodeBase64(data);
        //加密秘钥
        byte[] keyByte = Base64.decodeBase64(key);
        //偏移量
        byte[] ivByte = Base64.decodeBase64(iv);

        try {
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }

            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC7, "BC");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance(KEY_ALGORITHM);
            SecretKeySpec spec = new SecretKeySpec(keyByte, KEY_ALGORITHM);
            parameters.init(new IvParameterSpec(ivByte));

            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);

            if (null != resultByte && resultByte.length > 0) {
                String result = new String(wxdecode(resultByte), DEFAULT_CHARSET);
                return result;
            }
            return null;
        } catch (Exception e) {
            logger.warn("---------------------data=" + data);
            logger.warn("---------------------key=" + key);
            logger.warn("---------------------iv=" + iv);
            logger.warn("---------------------decrypt error and retry use AES/CBC/PKCS7Padding", e);
            logger.warn("=====================decrypt error log over==========================");
            IRedisService redisService = (IRedisService) SpringUtil.getBean("redisServiceImpl");
            if (!Strings.isNullOrEmpty(rkey)) redisService.del(rkey);
        }
        return null;
    }

    /**
     * 删除解密后明文的补位字符
     *
     * @param decrypted 解密后的明文
     * @return 删除补位字符后的明文
     */
    public static byte[] wxdecode(byte[] decrypted) {
        int pad = decrypted[decrypted.length - 1];
        if (pad < 1 || pad > 32) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }

    /**
     * 将数字转化成ASCII码对应的字符，用于对明文进行补码
     *
     * @param a 需要转化的数字
     * @return 转化得到的字符
     */
    public static char chr(int a) {
        byte target = (byte) (a & 0xFF);
        return (char) target;
    }

    /**
     * 生成加密秘钥
     *
     * @return
     */
    public static SecretKeySpec getSecretKey(final String key) {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        KeyGenerator kg = null;

        try {
            kg = KeyGenerator.getInstance(KEY_ALGORITHM);

            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(key.getBytes());

            //AES 要求密钥长度为 128
            kg.init(128, random);

            //生成一个密钥
            SecretKey secretKey = kg.generateKey();

            return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * AES加密操作
     *
     * @param data     待加密内容
     * @param password 加密密码
     * @return 返回Base64转码后的加密数据
     */
    public static String encrypt(String data, String password) {
        try {
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);// 创建密码器

            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器
            Base64 base64 = new Base64();
            byte[] result = cipher.doFinal(data.getBytes(DEFAULT_CHARSET));// 加密

//            return toHexStr(result);//通过Base64转码返回
            return Base64.encodeBase64String(result);//通过Base64转码返回
//            return parseByte2HexStr(result);//通过Base64转码返回
//            return new String(base64.encode(result),DEFAULT_CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * AES 解密操作
     *
     * @param data
     * @param password
     * @return
     */
    public static String decrypt(String data, String password) {

        try {
            //实例化
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            //使用密钥初始化，设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
            Base64 base64 = new Base64();
            //执行操作
            byte[] result = cipher.doFinal(Base64.decodeBase64(data));
//            byte[] result = cipher.doFinal(toHexByte(data));
            return new String(result, DEFAULT_CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 字节流转成十六进制表示
     */
    public static String toHexStr(byte[] src) {
        String strHex = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = 0; n < src.length; n++) {
            strHex = Integer.toHexString(src[n] & 0xFF);
            sb.append((strHex.length() == 1) ? "0" + strHex : strHex); // 每个字节由两个字符表示，位数不够，高位补0
        }
        return sb.toString().trim();
    }

    /**
     * 字符串转成字节流
     */
    public static byte[] toHexByte(String src) {
        int m = 0, n = 0;
        int byteLen = src.length() / 2; // 每两个字符描述一个字节
        byte[] ret = new byte[byteLen];
        for (int i = 0; i < byteLen; i++) {
            m = i * 2 + 1;
            n = m + 1;
            int intVal = Integer.decode("0x" + src.substring(i * 2, m) + src.substring(m, n));
            ret[i] = Byte.valueOf((byte) intVal);
        }
        return ret;
    }

    public static void main(String[] args) {

//        System.out.println(AesUtil.encrypt("1__1__6__t4f0uu7r",Constants.ENCODE_KEY));
        // 123456 encrypt=6KOdWd13x/UntnfstHEkFQ==
//        try {
//
//            System.out.println(AesUtil.encrypt("123456", "8wCN5ZMLiWklI1WnZ3/rYg==", "Nu2jEK/Ox9uLRxPvmgV5kg=="));
//        } catch (Exception e) {
//            System.out.println(e.getCause());
//        }

//        System.out.println(AesUtil.encrypt("1__1____7a9dfa3d__wx5419a52e4c448e13",Constants.ENCODE_KEY));
//        System.out.println(AesUtil.encrypt("1__1____789dfa3d",Constants.ENCODE_KEY));
//        System.out.println(AesUtil.encrypt("1__1__6__789dfa3d",Constants.ENCODE_KEY));
//        System.out.println(AesUtil.encrypt("1__1__6__789dfa3d",Constants.ENCODE_KEY));
////        return;
//
//        System.out.println(new Md5Hash("qn654321", "8d78869f470951332959580424d4bf4f",3).toString());
//        System.out.println(new SimpleHash("MD5", "qn654321", "8d78869f470951332959580424d4bf4f",3).toString());
//        User user = new User();
//        user.setUsername("admin");
//        user.setPassword("qn654321");
//        user.setSalt("8d78869f470951332959580424d4bf4f");
//        System.out.println(shiroMd5(user));

//
//        System.out.println(new Md5Hash("qn654321","admin8d78869f470951332959580424d4bf4f",2).toHex());
//        System.out.println(new Md5Hash("lc654321", "admin837135380675952641",2).toHex());
//        System.out.println(new Md5Hash("lc654321", "admin837135380675952641",3).toHex());
//        System.out.println(new Md5Hash("lc654321", "837135380675952641",2).toHex());
//        System.out.println(new Md5Hash("lc654321", "837135380675952641",3).toHex());
//        System.out.println(new Md5Hash("lc654321", "admin",2).toHex());
//        System.out.println(new Md5Hash("lc654321", "admin",3).toHex());
//        System.out.println(new SimpleHash("MD5","lc654321", "admin",2).toHex());
//        System.out.println(new SimpleHash("MD5","lc654321", "837135380675952641",2).toHex());
//        System.out.println(new SimpleHash("MD5","lc654321", "admin",3).toHex());
//        System.out.println(new SimpleHash("MD5","lc654321", "837135380675952641",3).toHex());
//        System.out.println(new SimpleHash("MD5","lc654321", "admin837135380675952641",3).toHex());


//
//        System.out.println(new Md5Hash("gushen888", "gushen839662116311633920",2).toHex());
//        System.out.println(new Md5Hash("gushen888", "gushen839662116311633920",3).toHex());
//        System.out.println(new Md5Hash("gushen888", "839662116311633920",2).toHex());
//        System.out.println(new Md5Hash("gushen888", "839662116311633920",3).toHex());
//        System.out.println(new Md5Hash("gushen888", "gushen",2).toHex());
//        System.out.println(new Md5Hash("gushen888", "gushen",3).toHex());
//
//        System.out.println(new Md5Hash("qn654321", "837135380675952641",2).toHex());
//        System.out.println(new Md5Hash("qn654321", "837135380675952641",3).toHex());
//        System.out.println(new Md5Hash("qn654321", "admin837135380675952641",2).toHex());
//        System.out.println(new Md5Hash("qn654321", "admin837135380675952641",3).toHex());
//        System.out.println(new Md5Hash("qn654321", "admin",2).toHex());
//        System.out.println(new Md5Hash("qn654321", "admin",3).toHex());

        String origin = "qnmall";
        String encrydata = "cWbMYVnlxrDR3y5PW6eidg==";
//        String key = "ZGIyMTM5NTYxYzlmZTA2OA==";
//        String iv = "ZGIyMTM5NTYxYzlmZTA8OA==";
//        String key = "940EwAN3t9sYtlo7SRkvpA==";
//        String iv = "YSo9EFx3wtdMfiRckiolag==";


//        String encrydata = "x3zTuy/vdv2RqK2BrLKc2Ker/PgRS3bLuozSwzUpt7+u/DyV4C9IppM/bZYiPRxgZK+l7/WUUCLBCNLXYv65PsnaiSXM1PLdBS+rTOO97Roo0eJvBV2uBIhgFjj93IhU+N2diw4uXofS6ORFacnMFHXHqXTYw1xGDmXTwKTGiugo2qvtY3CzhIzIW0LjZBlXW90XgbbsyKkroWccmhOOIwIiL7Fir9j+DjZipwpUzt8gpp2IwGUgV+ZsefZP16T+xroAhKsAyqQJ4tWR0j3K/TVcKYflK4LhEk5pzaxtVpzFoPGCD+/miKzpb40eJsZ0mq6y7JNTDPURR/YWWpFuNrsbdjIxNY86MvsE0cL0yw7pyryY1QQjt2+iO9qQvPzIrWvUoBEMiW78v+C8hJBt8CXp2JqViFCqLuqkASYEWwKdz9AjmbxsReSoO7M4iCjOIC4q5PX9Km3nqIsryrT0PQ==";
//        String key = "bSOpHHAzudq3Zjj0Tg5dzQ==";
//        String iv = "TO1WtaD3cV0xGMC1HrEb0A==";

//        String key = "67687077626b61647077637974717475";
//        String iv = "30343338343831323436373532323432";
        String result = "";

        try {
//            result = encrypt(origin, key, iv);
//            System.out.println(result);
//            System.out.println(decrypt(result, key, iv));

            // token 生成规则 uid+mcode+mid+random+apptype
//            result = encrypt("6__qnmall__1__f8e8g4y2", Constants.ENCODE_KEY);
//            result = encrypt("0__qnmall__1__f8e8g4y2", Constants.ENCODE_KEY);
//            result = encrypt("1__qnmall__0__f8e8g4y2", Constants.ENCODE_KEY);
//            result = encrypt("1__1____f8e8g4y2__wx5419a52e4c448e13__2", Constants.ENCODE_KEY);
//            result = encrypt("20__11__406__f8e8g4y2__wx0493500af01af077__2__3944", Constants.ENCODE_KEY);
            result = encrypt("1__8____f7e7a7f7__wxa965fd80bcdb1a8b__2", Constants.ENCODE_KEY);
            result = encodeURIComponent(result);
            result = encodeURIComponent(result);
            System.out.println(result);
            result = java.net.URLDecoder.decode(result, "UTF-8");

//            result = encrypt("huakouyun0168", Constants.ENCODE_PWD_KEY);
//            result = encrypt("guozhixian3065", Constants.ENCODE_PWD_KEY);
//            result = encrypt("shupinshangmao2035", Constants.ENCODE_PWD_KEY);
//            result = encrypt("shupinshangmao2360", Constants.ENCODE_PWD_KEY);


//            result = encrypt("shupinshangmao2360", key, iv);


            System.out.println(decrypt(result, Constants.ENCODE_KEY));
//            System.out.println(decrypt(result, key,iv,null));


//            System.out.println(decrypt(result, Constants.ENCODE_KEY));
//            System.out.println(decrypt("8Kdqjz3P4A+JocbzVedrwRFjddaoetCxBM2Bsse24aE=", Constants.ENCODE_KEY));
//            result = encrypt(origin, Constants.ENCODE_KEY);
//            System.out.println(result);
//            result = encrypt(origin+"beijing", Constants.ENCODE_KEY);
//            System.out.println(result);
//            System.out.println(decrypt(result, "qianniu"));

        } catch (Exception e) {

        }
    }


    public static String encodeURIComponent(String s) {
        String result = null;

        try {
            result = URLEncoder.encode(s, "UTF-8")
                    .replaceAll("\\+", "%20")
                    .replaceAll("\\%21", "!")
                    .replaceAll("\\%27", "'")
                    .replaceAll("\\%28", "(")
                    .replaceAll("\\%29", ")")
                    .replaceAll("\\%7E", "~");
        }

        // This exception should never occur.
        catch (UnsupportedEncodingException e) {
            result = s;
        }

        return result;
    }


}
